/*
 * Created on 21-ago-2004
 *
 */
package gurulab.maya.core.delivery;

import gurulab.maya.PassportConfigurationException;
import gurulab.maya.Receptor;
import gurulab.maya.common.AbsoluteApplicationAddress;
import gurulab.maya.common.MessageType;
import gurulab.maya.common.PhoneAddress;
import gurulab.maya.common.RelativeApplicationAddress;
import gurulab.util.net.https.HttpsHostNameVerifier;
import gurulab.util.net.https.HttpsX509TrustManager;
import gurulab.maya.data.DataManager;
import gurulab.maya.data.PassportDataManager;
import gurulab.merlin.core.CantHandException;
import gurulab.merlin.core.EnvelopeCantHandException;
import gurulab.merlin.core.PeerCantHandException;
import gurulab.merlin.core.envelope.AbstractEnvelopeProcessor;
import gurulab.merlin.core.envelope.Envelope;
import gurulab.merlin.server.EPContainer;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Properties;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.log4j.Logger;

/**
 * @author cobach
 *
 */
public class DeliveryEP extends AbstractEnvelopeProcessor{
    Logger logger = Logger.getLogger (DeliveryEP.class);
    
    private DataManager dm;
    private PreDispatchProcessor pp;
    private PassportDataManager pdm; //SANTIAGO VENTURA: MODIFICADO 08/11/2005
    
    private static String ERROR_RESPONSE = "Su solicitud no pudo ser procesada [m]";
    
    public DeliveryEP (EPContainer container,Properties props) throws Exception {
        super (container,props);
    }
    
    public void openConnection (String parametersFilePath) throws CantHandException {
        try {
            /*FileInputStream is = new FileInputStream (parametersFilePath);
            Properties prop = new Properties ();
            prop.load (is);
            */
            String driver = properties.getProperty ("db.driver");
            String dbmsURL = properties.getProperty ("db.dbmsURL");
            String username = properties.getProperty ("db.username");
            String password = properties.getProperty ("db.password");
            

            dm = new DataManager (driver, dbmsURL, username, password);
            pp = new PreDispatchProcessor (dm);
            pdm = new PassportDataManager (dm); //SANTIAGO VENTURA: MODIFICADO 08/11/2005
        } catch (Exception e) {
            throw new PeerCantHandException ("" + e);
        }
        
    }
    
    /*
     * (non-Javadoc)
     *
     * @see gurulab.merlin.core.AbstractEnvelopeProcessor#handEnvelope(gurulab.merlin.core.Envelope)
     */
    public Object handEnvelope (Envelope envelope) throws PeerCantHandException, CantHandException {
        String msg = "";
        PhoneAddress from;
        String result = null;
        AbsoluteApplicationAddress app;
        
        if (!(envelope.getOrigin () instanceof PhoneAddress)) {
            throw new EnvelopeCantHandException ("Can't hand Envelope with origin of type: " + envelope.getOrigin ().getClass ().getName ());
        }
        
        if (!(envelope.getDestination () instanceof RelativeApplicationAddress)) {
            throw new EnvelopeCantHandException ("Can't hand an Envelope with a destination different from RelativeApplicationAddress");
        }
        
        app = pp.preProcess (envelope);
        
        from = (PhoneAddress) envelope.getOrigin ();
        //SANTIAGO VENTURA: MODIFICADO 08/11/2005
        logger.debug ("Envelope was received to dispatch to the application:" + app.toString () + "with the passport " + app.getPassport ());
        
        if (app.getProtocol ().equalsIgnoreCase ("WS")) {
            try {
                //SANTIAGO VENTURA: MODIFICADO 08/11/2005
                //logger.info("Web service invocation is being done.");
                result = webServiceInvoke (app.getURL (), app.getKey (), from.getcountrycode (), from.getareacode (), from.getlocal (), envelope.getContent ().toString (), "" + envelope.getId () + "");
                logger.info ("Web service invocation was successful and reply: " + result);
                if (isSimpleMode (app.getPassport ())) {
                    logger.error ("The delivery scheme is simple mode. Reply is not expected.");
                    //Se aado el retorno del Envelope 17032006
                    return envelope;
                }
            } catch (Throwable e) {
                result = ERROR_RESPONSE;
                logger.error ("Web service invocation has failed:" + e);
                throw new PeerCantHandException(e);
            }
        } else if (app.getProtocol ().equalsIgnoreCase ("CGI")) {
            try {
                //SANTIAGO VENTURA: MODIFICADO 01/03/2006
                logger.info ("Calling to CGI:" + app.getURL ());
                result = enviarCGI (app.getURL (), app.getKey (), from.getCountryCode (), from.getAreaCode (), from.getLocal (), envelope.getContent ().toString (), "" + envelope.getId () + "");
                logger.info ("CGI invocation was successful and reply: " + result);
                if (isSimpleMode (app.getPassport ())) {
                    logger.error ("The delivery scheme is simple mode. Reply is not expected.");
                    //Se aado el retorno del Envelope 17032006
                    return envelope;
                }
            } catch (Throwable e) {
                result = ERROR_RESPONSE;
                logger.error ("CGI service invocation has failed:" + e);
                throw new PeerCantHandException(e);
            }
        } else {
            throw new PassportConfigurationException ("The protocol of the application must be defined 'CGI' or 'WS'");
        }
        
        if (result == null) result = ERROR_RESPONSE;
        
        
        
        Envelope reply = envelope.replyTo (result.trim ());
        reply.getOrigin ().setVariable (app.getPassport ());
        //reply.setComment ("Interactivo");
        reply.setComment (MessageType.SMS_INTERACTIVE);
        //Se aado el retorno del Envelope 17032006
        return getEnvelopeHandler ().handEnvelope (reply);
    }
    
    private String enviarCGI (String url, String clave, String codPais, String codArea,  String numRem, String mensaje, String idMensaje) throws Exception{
        String tempResponse = "";
        try {
            String mensajeEncoded=URLEncoder.encode (mensaje,"UTF-8");
            String direccionIn = url.concat ("?clave="+URLEncoder.encode (clave,"UTF-8")+"&codPais="+codPais+"&codArea="+codArea+"&numRem="+numRem+"&mensaje="+mensajeEncoded+"&idMensaje="+idMensaje);
            
            //1. Open the connection and keep it open in the instance of this class
            logger.info ("Connection:"+direccionIn);
            //HttpURLConnection conn;
            URL direccion = new URL (direccionIn);
            //URLConnection conexion = direccion.openConnection();
            //conexion.setDoOutput(true);
            
            if (url.startsWith ("https")){
                javax.net.ssl.HttpsURLConnection conn = (javax.net.ssl.HttpsURLConnection) direccion.openConnection ();
                // Init trust managers so the authenticity of the certificate is in our
                // control.
                X509TrustManager oTrustMngr = new HttpsX509TrustManager ();
                TrustManager oEnlistaTrustManagers[] = { oTrustMngr };
                SSLContext ctx = SSLContext.getInstance ("SSL");
                ctx.init (null, oEnlistaTrustManagers, null);
                SSLSocketFactory sf = ctx.getSocketFactory ();
                conn.setSSLSocketFactory (sf);
                conn.setHostnameVerifier (new HttpsHostNameVerifier ());
                
                conn.setDoInput (true);
                conn.setDoOutput (true);
                conn.setUseCaches (false);

                //Seteando el timeout del archivo de propiedades
                if(properties.getProperty ("request.timeout")!=null){
                	conn.setReadTimeout(Integer.parseInt(properties.getProperty ("request.timeout")));
                }
                conn.setRequestMethod ("GET");
                InputStreamReader isr = new InputStreamReader (conn.getInputStream ());
                BufferedReader br = new BufferedReader (isr);
                String temp;
                while ((temp = br.readLine ()) != null)
                    tempResponse = tempResponse + temp;
                
                br.close ();
                isr.close ();
                conn.disconnect ();
            } else {
                HttpURLConnection conn = (HttpURLConnection)direccion.openConnection ();
                conn.setRequestMethod ("GET");
                //Seteando el timeout del archivo de propiedades
                if(properties.getProperty ("request.timeout")!=null){
                	conn.setReadTimeout(Integer.parseInt(properties.getProperty ("request.timeout")));
                }
                InputStreamReader isr = new InputStreamReader (conn.getInputStream ());
                BufferedReader br = new BufferedReader (isr);
                String temp;
                while ((temp = br.readLine ()) != null)
                    tempResponse = tempResponse + temp;
                
                br.close ();
                isr.close ();
                conn.disconnect ();
            }
        }catch(SocketTimeoutException timeOutExc){
        	logger.info("Timeout for "+url);
        	throw new Exception (timeOutExc.getMessage ());
        }catch(Exception e){
            e.printStackTrace ();
            throw new Exception (e.getMessage ());
        }
        return(tempResponse);
    }
    
    
    /**
     * SANTIAGO VENTURA: INCLUIDO 08/11/2005 Este mtodo hace un validacin del esquema de entrega
     * usado por el web service a ser invocado. El esquema de entrega puede ser modo simple (asincrono)
     * o respuesta inmediata o piggyback (sincrono).
     *
     * @param passport
     * @return false if is piggyback true if is simple mode.
     * @throws Exception
     */
    public boolean isSimpleMode (String passport) throws Exception {
        String msg = "";
        String deliveryScheme = "";
        boolean singleWay = false;
        if (passport == null || passport.equals ("")) {
            if(logger.isDebugEnabled ()) {
                msg = "The delivery scheme cannot be defined.";
                logger.debug (msg);
            }
            throw new Exception ("The delivery scheme cannot be defined.");
        }
        try {
            deliveryScheme = pdm.getDeliverySchema (passport);
            if(logger.isDebugEnabled ()) {
                msg = "The delivery scheme is " + deliveryScheme;
                logger.debug (msg);
            }
        } catch (Exception e) {
            throw new Exception ("The delivery scheme cannot be defined for the passport " +passport+" "+e.toString ());
        }
        
        if (deliveryScheme.equalsIgnoreCase ("piggyback")) return singleWay;
        else singleWay = true;
        
        return (singleWay);
    }
    
    public String webServiceInvoke (String url, String clave, int codPaisRem, int codAreaRem, int numRem, String mensaje, String idMensaje) throws Exception {
        Receptor rec = ReceptorHelper.bind (url);
        return (rec.recibeSMS (clave, codPaisRem, codAreaRem, numRem, mensaje, idMensaje));
    }
    
    /* (sin Javadoc)
     * @see gurulab.merlin.core.EnvelopeHandler#retrieveEnvelope(java.lang.Object)
     */
    public void retrieveEnvelope (Object EnvelopeReference) throws CantHandException {
        // TODO Apndice de mtodo generado automticamente
        
    }
    
    /* (sin Javadoc)
     * @see gurulab.merlin.core.EnvelopeHandler#commitHandEnvelope(java.lang.Object)
     */
    public void commitHandEnvelope (Object EnvelopeReference) throws CantHandException {
        // TODO Apndice de mtodo generado automticamente
        
    }
    
    public boolean estaOcupado() {
    	// TODO Auto-generated method stub
    	return false;
    }
    
}